import 'dart:collection';

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:share_fit/app/controllers/base/base_controller.dart';
import 'package:share_fit/app/helper/db/ute_db_helper.dart';
import 'package:share_fit/app/helper/ext/tr_ext.dart';
import 'package:share_fit/app/models/home/sleep_model.dart';
import 'package:share_fit/app/routes/home_routes.dart';
import 'package:share_fit/app/tools/locale_tool.dart';
import 'package:share_fit/app/tools/date_tool.dart';
import 'package:share_fit/app/tools/log_utils.dart';
import 'package:share_fit/generated/locales.g.dart';
import 'package:share_fit/app/models/home/date_type_enum.dart';

import 'state.dart';

class SleepLogic extends BaseController {
  final SleepState state = SleepState();

  changeDateType({type, int? selectedIndex}) {
    state.selectedDateType.value = type;
    state.dateTypeBarIndex = selectedIndex ?? 0;

    update();

    if (type == DateType.year) {
      for (var month = 1; month <= 12; month++) {
        // state.dates.add('$month');
      }
    } else if (type == DateType.week) {
      List<DateTime> dts = type.getWeekDays;
      _queryWeekData(dts.first, dts.last.add(const Duration(days: 1)));
    } else if (type == DateType.month) {
      List<DateTime> dts = type.getMonthDays;
      _queryMonthData(dts.first, dts.last.add(const Duration(days: 1)));
    } else {
      _queryTodayData(state.selectedDate);
    }

    update(['calendar_btn_icon', 'naps_container', 'date_type_bar']);
  }

  /// 右上角日历选择
  datePicker(dt) {
    if (dt != null) {
      state.selectedDate = dt;
      state.selectedDateType.value = DateType.day;

      update(['calendar_btn_icon']);

      _queryTodayData(state.selectedDate);
    }
  }

  goBack() {
    Get.back();
  }

  @override
  String getRemotePath() {
    return "";
  }

  @override
  void initState() {
    getSleepReportData();

    _queryTodayData(DateTime.now());
    _loadDateListOfRecord();
  }

  @override
  void onRemoteEvent(event, method) {
    // TODO: implement onRemoteEvent
  }

  _updateNapsContainer(List<SleepModel> list) {
    state.napList = list;
    update(['naps_container']);
  }

  _updateBarChartContainer(List<SleepModel> list) {
    state.barChartDataList = list;
    update(['sleep_bar_chart']);
  }

  _updateAnalysisContainer(List<SleepModel> list) {
    state.dataList = list;
    update(['sleep_analysis']);
  }

  Future<void> _queryTodayData(DateTime today) async {
    DateTime temp = today.add(const Duration(days: -1));
    DateTime start = DateTime(temp.year, temp.month, temp.day, 20, 0, 0);
    DateTime end = DateTime(today.year, today.month, today.day, 20, 0, 0);

    _updateDateRange(start: null, end: end);

    String s = DateTool.getYMDHMSStr(start);
    String e = DateTool.getYMDHMSStr(end);

    UteDbHelper.getDB().then((db) {
      db.sleepDao.findForBarChart(getAddress(), s, e).then((list) => _updateBarChartContainer(list));
      db.sleepDao.findTodayData(getAddress(), s, e).then((list) => _updateAnalysisContainer(list));
      db.sleepDao.findNaps(getAddress(), s, e).then((list) => _updateNapsContainer(list));
    });
  }

  Future<void> _queryWeekData(DateTime start, DateTime end) async {
    // 当天的数据是从前一天的晚上20点至当天的晚上20点
    DateTime sTemp = start.add(const Duration(days: -1));
    start = DateTime(sTemp.year, sTemp.month, sTemp.day, 20, 0, 0);
    DateTime eTemp = end.add(const Duration(days: -1));
    end = DateTime(eTemp.year, eTemp.month, eTemp.day, 20, 0, 0);

    _updateDateRange(start: start.add(const Duration(days: 1)), end: end);

    String s = DateTool.getYMDHMSStr(start);
    String e = DateTool.getYMDHMSStr(end);

    UteDbHelper.getDB().then((db) {
      db.sleepDao.findInRangeData(getAddress(), s, e).then((list) => _onDaysDataResult(list, start, end, true));
    });
  }

  Future<void> _queryMonthData(DateTime start, DateTime end) async {
    // 当天的数据是从前一天的晚上20点至当天的晚上20点
    DateTime sTemp = start.add(const Duration(days: -1));
    start = DateTime(sTemp.year, sTemp.month, sTemp.day, 20, 0, 0);
    DateTime eTemp = end.add(const Duration(days: -1));
    end = DateTime(eTemp.year, eTemp.month, eTemp.day, 20, 0, 0);

    _updateDateRange(start: start.add(const Duration(days: 1)), end: end);

    String s = DateTool.getYMDHMSStr(start);
    String e = DateTool.getYMDHMSStr(end);

    UteDbHelper.getDB().then((db) {
      db.sleepDao.findInRangeData(getAddress(), s, e).then((list) => _onDaysDataResult(list, start, end, false));
    });
  }

  inDateRange(SleepModel ref, DateTime left, DateTime right) {
    DateTime dt = DateTime.fromMillisecondsSinceEpoch(ref.startTime! * 1000);
    return DateTool.isInRange(left, right, dt);
  }

  _onDaysDataResult(List<SleepModel> list, DateTime start, DateTime end, bool isWeek) {
    state.dataList.clear();
    String langCode = LocaleTool.locale.split('_')[0];

    int days = end.difference(start).inDays;
    for (int i = 0; i < days; i++) {
      // 开始时间从前一天20点
      DateTime sTemp = start.add(Duration(days: i));
      // 结束时间当天20点
      DateTime eTemp = sTemp.add(const Duration(days: 1));

      String calendar = isWeek ? DateFormat.E(langCode).format(eTemp) : DateTool.mdFormat(eTemp);

      List<SleepModel> matched = list.where((e) => inDateRange(e, sTemp, eTemp)).toList();
      if (matched.isNotEmpty) {
        matched.forEach((e) => e.calendar = calendar);
        state.dataList.addAll(groupByDate(matched));
      }

      if (matched.isEmpty) {
        List<SleepModel> sms = state.sleepTypes.map((e) => SleepModel(calendar: calendar, sleepType: e.$1, sleepTime: 0)).toList();
        state.dataList.addAll(sms);
      }
    }

    getData();

    update(['sleep_analysis', 'sleep_bar_chart']);
  }

  List<SleepModel> groupByDate(List<SleepModel> list) {
    Map<String, List<SleepModel>> group = {for (var e in list) e.sleepType.toString(): list.where((item) => item.sleepType == e.sleepType).toList()};

    List<SleepModel> result = [];
    group.forEach((key, value) {
      int sleepTime = 0;
      int st = DateTime.now().millisecondsSinceEpoch ~/ 1000;
      int et = 0;
      String calendar = '';
      for (var element in value) {
        sleepTime += element.sleepTime ?? 0;
        calendar = element.calendar!;

        if (element.startTime! < st) {
          st = element.startTime!;
        }

        if (element.endTime! > et) {
          et = element.endTime!;
        }
      }

      result.add(SleepModel(sleepType: int.parse(key), sleepTime: sleepTime, startTime: st, endTime: et, calendar: calendar));
    });

    return result;
  }

  /// 周月图标所需的数据
  void getData() {
    Map<String, List<SleepModel>> map = {
      for (var e in state.dataList) e.calendar!: state.dataList.where((item) => item.calendar == e.calendar).toList()
    };
    state.sleepSummaries = map.entries.map((e) => SleepSummary(calendar: e.key, models: e.value)).toList();
  }

  void _updateDateRange({DateTime? start, required DateTime end}) {
    if (start == null) {
      state.dateRangeText = DateTool.ymdFormat(end);
    } else {
      state.dateRangeText = '${DateTool.ymdFormat(start)} ~ ${DateTool.ymdFormat(end)}';
    }
    update(['sleep_date_range']);
  }

  Future<void> getSleepSummaryData() async {
    final db = await UteDbHelper.getDB();
    SleepModel sleepData = await db.sleepDao.findSleepTimeSum(getAddress(), getDateString()) ?? SleepModel();
    SleepSummary ss = sleepData.sleepSummary;
    state.sleepSummary = ss;
    logger.d('sleep data summary : $ss');
    update(['sleep_summary']);
  }

  Future<void> getSleepAvgData() async {
    final db = await UteDbHelper.getDB();
    List<SleepModel> sleepModels = await db.sleepDao.findAvg(getAddress(), getDateString());
    print(sleepModels.length);
    List<SleepSummary> sleepAvg = List.generate(24, (index) => SleepSummary());
    for (int i = 0; i < sleepModels.length; i++) {
      SleepModel element = sleepModels[i];
      SleepSummary sleep = SleepSummary();
      // sleep.calendar = element.calendar ?? '';
      // sleep.sleepTime = element.sleepTime ?? 0;
      // sleep.sleepType = element.sleepType ?? 0;
      int index = int.parse(element.calendar!.split(':')[0]);
      sleepAvg[index] = sleep;
      // print('======>>> ${sleepModels[i].toMap()}');
    }

    // state.todaySleepData = sleepAvg;
    update(['sleep_avg']);
  }

  Future<void> getSleepReportData() async {
    final db = await UteDbHelper.getDB();
    List<SleepModel> sleeps = await db.sleepDao.findForReport(getAddress(), getDateString(), getDateString());
    print('sleeps length:${sleeps.length}');
    List<SleepSummary> sleepSummarys = [];
    for (var element in sleeps) {
      SleepSummary summary = SleepSummary();
      // summary.calendarTime = element.calendarTime!;
      // summary.sleepTime = element.sleepTime!;
      // summary.sleepType = element.sleepType!;
      sleepSummarys.add(summary);
    }
    state.sleepSummaries = sleepSummarys;
    update(['sleep_report']);
  }

  String getDateString() {
    return DateFormat('yyyyMMdd').format(DateTime.now());
  }

  /// 将有心率数据的日期标记在日历上
  onCalendarEventLoad(DateTime day) {
    Map<DateTime, List<bool>> events = LinkedHashMap<DateTime, List<bool>>(
      equals: DateTool.isSameDay,
      hashCode: (key) => key.day * 1000000 + key.month * 10000 + key.year,
    )..addAll(state.dateListOfRecord);

    return events[day] ?? [];
  }

  /// 查找指定年份中有血氧记录的日期
  Future<void> _loadDateListOfRecord() async {
    DateTime now = DateTime.now();
    DateTime lastYear = DateUtils.addMonthsToMonthDate(now, -12);
    UteDbHelper.getDB().then((db) => //
        db.sleepDao.findHasRecordDays(getAddress(), lastYear.year.toString(), now.year.toString()).then((result) {
          state.dateListOfRecord.clear();
          result.forEach((element) {
            state.dateListOfRecord.putIfAbsent(DateTime.parse(element), () => [true]);
          });
        }));
  }

  goDeepSleepPage() {
    Get.toNamed(HomeRoutes.deepSleepPage, arguments: {
      'value': state.dsRefValue.toInt(),
      'level': state.dsRefValueLevelText.data,
      'color': state.dsRefValueLevelText.style?.color,
    });
  }

  goLightSleepPage() {
    Get.toNamed(HomeRoutes.lightSleepPage, arguments: {
      'value': state.lsRefValue.toInt(),
      'level': state.lsRefValueLevelText.data,
      'color': state.lsRefValueLevelText.style?.color,
    });
  }

  goRemSleepPage() {
    Get.toNamed(HomeRoutes.remPage, arguments: {
      'value': state.remRefValue.toInt(),
      'level': state.remRefValueLevelText.data,
      'color': state.remRefValueLevelText.style?.color,
    });
  }

  goAwakeSleepPage() {
    Get.toNamed(HomeRoutes.awakePage, arguments: {
      'value': state.awakeTimes ~/ state.validDataLength,
      'level': state.awakeRefValueLevelText.data,
      'color': state.awakeRefValueLevelText.style?.color,
    });
  }

  goNapSleepPage({SleepModel? model}) {
    model ??= SleepModel(sleepTime: state.napsSleepSum ~/ state.validDataLength);
    Get.toNamed(HomeRoutes.napPage, arguments: model);
  }

  goSleepDurationPage() {
    Get.toNamed(HomeRoutes.durationSleepPage, arguments: {
      'value': state.sleepSumWithoutAwake ~/ state.validDataLength,
      'level': state.totalRefValueLevelText.data,
      'color': state.totalRefValueLevelText.style?.color,
    });
  }

  String getDeepSleepTitle() {
    return state.selectedDateType.value == DateType.day
        ? LocaleKeys.sleep_deep_text.ts
        : '${LocaleKeys.sleep_avg_text.ts}${LocaleKeys.sleep_deep_text.ts}';
  }

  String getLightSleepTitle() {
    return state.selectedDateType.value == DateType.day
        ? LocaleKeys.sleep_light_text.ts
        : '${LocaleKeys.sleep_avg_text.ts}${LocaleKeys.sleep_light_text.ts}';
  }

  String getRemSleepTitle() {
    return state.selectedDateType.value == DateType.day
        ? LocaleKeys.sleep_rem_text.ts
        : '${LocaleKeys.sleep_avg_text.ts}${LocaleKeys.sleep_rem_text.ts}';
  }

  String getAwakeTitle() {
    return state.selectedDateType.value == DateType.day
        ? LocaleKeys.sleep_sober_text.ts
        : '${LocaleKeys.sleep_avg_text.ts}${LocaleKeys.sleep_sober_text.ts}';
  }

  getTotalDurationText() {
    return state.selectedDateType.value == DateType.day
        ? LocaleKeys.sleep_total_title_text.ts
        : '${LocaleKeys.sleep_avg_text.ts}${LocaleKeys.sleep_total_title_text.ts.toLowerCase()} ';
  }

  getDeepSleepRatioText() {
    return state.selectedDateType.value == DateType.day
        ? LocaleKeys.sleep_deep_ratio_title_text.ts
        : '${LocaleKeys.sleep_avg_text.ts}${LocaleKeys.sleep_deep_ratio_title_text.ts.toLowerCase()}';
  }

  getLightSleepRatioText() {
    return state.selectedDateType.value == DateType.day
        ? LocaleKeys.sleep_light_ratio_title_text.ts
        : '${LocaleKeys.sleep_avg_text.ts}${LocaleKeys.sleep_light_ratio_title_text.ts.toLowerCase()}';
  }

  getRemSleepRatioText() {
    return state.selectedDateType.value == DateType.day
        ? LocaleKeys.sleep_rem_ratio_title_text.ts
        : '${LocaleKeys.sleep_avg_text.ts}${LocaleKeys.sleep_rem_ratio_title_text.ts.toLowerCase()}';
  }

  getAwakeRatioText() {
    return state.selectedDateType.value == DateType.day
        ? LocaleKeys.sleep_awake_times_title_text.ts
        : '${LocaleKeys.sleep_avg_text.ts}${LocaleKeys.sleep_awake_times_title_text.ts.toLowerCase()}';
  }

  getNapRatioText() {
    return state.selectedDateType.value == DateType.day
        ? LocaleKeys.sleep_naps_text.ts
        : '${LocaleKeys.sleep_avg_text.ts}${LocaleKeys.sleep_naps_text.ts.toLowerCase()}';
  }
}
